
import indexing.featureindex
from new import classobj
from genome.genomereader import GenomeReader

baseComplements = { "A":"T", "T":"A", "C":"G", "G":"C", "N":"N", "a":"t", "t":"a", "g":"c", "c":"g", "n":"n" }

class SkipZeroes:
    LEFT = 1
    RIGHT = 2
    INTERNAL = 4
    NONE = 0
    ALL = 7

class SinglePositionProcessor:
    def __init__(self):
        self.total = 0

    def incr(self, base, seqpos, details):
        self.total += 1

class SummaryProcessor:
    def __init__(self, ProcessorClass, DummyProcessorClass=None, positionChecker=None):
        if not isinstance(ProcessorClass, classobj) or not isinstance(ProcessorClass(), SinglePositionProcessor):
            raise Exception("processor must be a class object derived from SinglePositionProcessor")
        self.ProcessorClass = ProcessorClass
        self.DummyProcessorClass = DummyProcessorClass
        self.positionChecker = positionChecker
        self.readerChr = None
        self.reader = None
        
    def getProcessor(self, chr, pos):
        if not self.positionChecker or self.positionChecker.check(chr, pos):
            pc = self.ProcessorClass()
            if not self.reader or self.readerChr != chr:
                self.reader = GenomeReader(chr)
                self.readerChr = chr
            pc.genomicBase = self.reader[pos]
            return pc
        else:
            return self.DummyProcessorClass()
    
    def process(self, query, indexFilename, skipZeroes=SkipZeroes.ALL):
        '''
        '''
        skipInternalZeroes = skipZeroes & SkipZeroes.INTERNAL
        skipLeadingZeroes = skipZeroes & SkipZeroes.LEFT
        skipTrailingZeroes = skipZeroes & SkipZeroes.RIGHT
  
        (qchr, qstrand, qmin, qmax) = query

        vals = [self.getProcessor(qchr, qmin - 100 + x) for x in xrange(100)]
        valsMin = qmin - 100

        inLeadingZeroes = skipLeadingZeroes

        if isinstance(indexFilename, indexing.featureindex.FeatureIndex):
            # passed an index
            featureGenerator = indexFilename.getFeatures((qchr, qmin, qmax))
        else:
            # passed an index filename
            featureGenerator = indexing.featureindex.getFeatures(indexFilename, (qchr, qmin, qmax))
        
        for feature in featureGenerator:
            # about to add counts to [spos, epos], so positions left of spos
            # are done.  while valsMin < spos, return a single value and update.
            while valsMin < feature.spos:
                currPosition = vals[0]
                inLeadingZeroes = (inLeadingZeroes and not currPosition.total)
                inInternalZeroes = (skipInternalZeroes and not currPosition.total)
                if valsMin>=qmin and valsMin<=qmax and not inLeadingZeroes and not inInternalZeroes:
                    yield (valsMin, currPosition)
                    valsMin += 1
                    vals = vals[1:] + [self.getProcessor(qchr, valsMin + len(vals) - 1)]
                else:
                    vals = vals[1:]
                    valsMin += 1
                    if not vals:
                        valsMin = feature.spos

            while len(vals) < 100:
                vals.append(self.getProcessor(qchr, valsMin + len(vals)))

            details = feature.getDetails()
            seq = details.seq
            seqLength = feature.epos - feature.spos + 1
            dx = int(details.startDrop)
            
            if details.strand == '+':
                for x in xrange(0, seqLength):
                    if details.phredQuals:
                        phc = details.phredQuals[x]
                    else:
                        phc = '@'
                    vals[feature.spos + x - valsMin].incr((seq[x], phc), x + dx, details)
            else:
                for x in xrange(0, seqLength):
                    if details.phredQuals:
                        phc = details.phredQuals[seqLength-1-x]
                    else:
                        phc = '@'
                    vals[feature.spos + x - valsMin].incr((baseComplements[seq[seqLength-1-x]], phc), seqLength-1-x + dx, details)

        # return remaining non-zero values
        while valsMin <= qmax and len(vals) > 0:
            currPosition = vals[0]
            inTrailingZeroes = (skipTrailingZeroes and not currPosition.total)
            if valsMin>=qmin and not inTrailingZeroes:
                yield (valsMin, currPosition)
            vals = vals[1:]
            valsMin += 1

        # return zeroes until qmax if that was requested
        if not skipTrailingZeroes:
            while valsMin <= qmax:
                if valsMin>=qmin:
                    yield (valsMin, self.getProcessor(qchr, valsMin))
                valsMin += 1

